package HGin

import (
	"github.com/gin-gonic/gin"
	"reflect"
	"strings"
)

type RouteGroup interface {
	// Middleware 设置局部中间件
	Middleware(...gin.HandlerFunc) RouteGroup
	Bind(objs ...interface{}) RouteGroup
	Prefix(prefix string) RouteGroup
}

type group struct {
	instance   *gin.Engine
	middleware []gin.HandlerFunc
	prefix     string
}

func (g *group) Prefix(prefix string) RouteGroup {
	if prefix != "" {
		g.prefix += "/" + strings.Trim(prefix, "/")
	}
	return g
}

func NewRouteGroup(engine *gin.Engine) RouteGroup {
	return &group{
		instance:   engine,
		middleware: make([]gin.HandlerFunc, 0, 5),
		prefix:     "",
	}
}

func (g *group) Middleware(handlerFunc ...gin.HandlerFunc) RouteGroup {
	//TODO implement me
	if len(handlerFunc) > 0 {
		g.middleware = append(g.middleware, handlerFunc...)
	}
	return g
}

// Bind 绑定结构体
func (g *group) Bind(objs ...interface{}) RouteGroup {
	if len(objs) < 0 {
		return g
	}
	g.bindStruct(objs...)
	return g
}

// 反射结构体解析路由
func (g *group) bindStruct(objs ...interface{}) {
	for _, obj := range objs {
		// 反射
		v := reflect.ValueOf(obj)

		isStructPtr := v.Kind() == reflect.Pointer && v.Elem().Kind() == reflect.Struct // 结构体指针
		isStruct := v.Kind() == reflect.Struct                                          // 结构体
		if !(isStructPtr || isStruct) {
			continue
		}

		vStruct := v
		if isStructPtr {
			vStruct = v.Elem()
		}

		prefix, handlerFn, methods := parseStruct(vStruct)

		if g.prefix != "" {
			prefix = get_prefix(g.prefix + prefix)
		}

		// 只有传入的是结构体的字段属性
		if isStruct {
			v = reflect.New(v.Type())
		}

		if v.NumMethod() == 0 {
			continue
		}

		// 路由地址是: 包名/文件名/操作
		// 全部都是小写
		if v.Type().String() == "" {
			continue
		}

		// 合并中间件
		if len(g.middleware) > 0 {
			handlerFn = append(g.middleware, handlerFn...)
		}

		for i := 0; i < v.NumMethod(); i++ {
			// 检测这个方法是否已导出
			if !v.Type().Method(i).IsExported() {
				continue
			}

			// 原始方式调用
			path := convertPath(prefix, strings.TrimLeft(v.Type().String(), "*")+"."+v.Type().Method(i).Name)
			if v.Method(i).Type().String() == "func(*gin.Context)" {
				g.instance.Match(getActionMethod(v.Type().Method(i).Name, methods), path, append(handlerFn, defaultFun(v.Method(i)))...)
			}
		}

	}
}
